package com.pick.base.aspect;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.pick.base.util.IPUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pick.base.annotation.OperationLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;

/**
 * 操作日志切面
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Autowired
    private ObjectMapper objectMapper;

    @Around("@annotation(operationLog)")
    public Object around(ProceedingJoinPoint point, OperationLog operationLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
        
        // 获取方法信息
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String className = point.getTarget().getClass().getSimpleName();
        String methodName = method.getName();
        
        // 获取用户信息
        String username = "anonymous";
        try {
            if (StpUtil.isLogin()) {
                username = StpUtil.getLoginIdAsString();
            }
        } catch (Exception e) {
            // 忽略获取用户信息失败的情况
        }
        
        // 记录操作开始
        log.info("操作开始 - 用户: {}, 模块: {}, 操作: {}, 方法: {}.{}", 
                username, operationLog.module(), operationLog.type(), className, methodName);
        
        // 记录请求参数
        if (operationLog.recordParams() && point.getArgs().length > 0) {
            try {
                String params = objectMapper.writeValueAsString(Arrays.asList(point.getArgs()));
                log.info("请求参数: {}", params);
            } catch (Exception e) {
                log.warn("记录请求参数失败: {}", e.getMessage());
            }
        }
        
        Object result = null;
        Exception exception = null;
        
        try {
            // 执行目标方法
            result = point.proceed();
            return result;
        } catch (Exception e) {
            exception = e;
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            
            // 记录操作结果
            if (exception == null) {
                log.info("操作成功 - 用户: {}, 模块: {}, 操作: {}, 耗时: {}ms", 
                        username, operationLog.module(), operationLog.type(), executionTime);
                
                // 记录返回结果
                if (operationLog.recordResult() && result != null) {
                    try {
                        String resultStr = objectMapper.writeValueAsString(result);
                        log.info("返回结果: {}", resultStr);
                    } catch (Exception e) {
                        log.warn("记录返回结果失败: {}", e.getMessage());
                    }
                }
            } else {
                log.error("操作失败 - 用户: {}, 模块: {}, 操作: {}, 耗时: {}ms, 错误: {}", 
                        username, operationLog.module(), operationLog.type(), executionTime, exception.getMessage());
            }
            
            // 这里可以扩展保存到数据库的逻辑
            saveOperationLog(username, operationLog, className, methodName, 
                    request, executionTime, exception == null, exception);
        }
    }
    
    @Autowired
    private ApplicationContext applicationContext;
    
    /**
     * 保存操作日志到数据库
     */
    private void saveOperationLog(String username, OperationLog operationLog, 
                                String className, String methodName,
                                HttpServletRequest request, long executionTime, 
                                boolean success, Exception exception) {
        try {
            // 动态获取SysOperationLogService，避免循环依赖
            Object operationLogService = applicationContext.getBean("sysOperationLogServiceImpl");

            // 使用反射创建操作日志实体
            Class<?> logEntityClass = Class.forName("com.pick.admin.entity.SysOperationLog");
            Object logEntity = logEntityClass.getDeclaredConstructor().newInstance();
            
            // 设置基本信息
            setField(logEntity, "username", username);
            setField(logEntity, "module", operationLog.module());
            setField(logEntity, "operationType", operationLog.type());
            setField(logEntity, "description", operationLog.description());
            setField(logEntity, "method", className + "." + methodName);
            setField(logEntity, "executionTime", executionTime);
            setField(logEntity, "status", success ? 1 : 0);
            setField(logEntity, "operationTime", LocalDateTime.now());
            
            // 设置请求信息
            if (request != null) {
                setField(logEntity, "requestUrl", request.getRequestURL().toString());
                setField(logEntity, "requestIp", IPUtil.getClientIP(request));
                
                // 记录请求参数（限制长度）
                if (operationLog.recordParams()) {
                    try {
                        String params = objectMapper.writeValueAsString(Collections.singletonList(request.getParameterMap()));
                        if (params.length() > 2000) {
                            params = params.substring(0, 2000) + "...";
                        }
                        setField(logEntity, "requestParams", params);
                    } catch (Exception e) {
                        setField(logEntity, "requestParams", "参数记录失败: " + e.getMessage());
                    }
                }
            }
            
            // 设置错误信息
            if (exception != null) {
                String errorMsg = exception.getMessage();
                if (StrUtil.isNotBlank(errorMsg) && errorMsg.length() > 500) {
                    errorMsg = errorMsg.substring(0, 500) + "...";
                }
                setField(logEntity, "errorMsg", errorMsg);
            }
            
            // 调用保存方法
            Method saveMethod = operationLogService.getClass().getMethod("saveOperationLog", logEntityClass);
            saveMethod.invoke(operationLogService, logEntity);
            
        } catch (Exception e) {
            log.warn("保存操作日志失败: {}", e.getMessage());
        }
    }
    
    /**
     * 使用反射设置字段值
     */
    private void setField(Object obj, String fieldName, Object value) {
        try {
            java.lang.reflect.Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            // 忽略字段设置失败
        }
    }
}